﻿
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading
Imports osi.root.constants
Imports osi.root.connector
Imports osi.root.delegates
Imports osi.root.envs
Imports osi.root.formation
Imports osi.root.utils
Imports osi.root.procedure
Imports osi.root.lock
Imports osi.service.argument
Imports osi.service.device
Imports osi.service.convertor
Imports osi.service.commander
Imports counter = osi.root.utils.counter

Partial Public Class powerpoint
    Implements new_device_event_isync_device_pool(Of TcpClient), 
               new_device_event_isync_device_pool(Of flow)
    Public ReadOnly token As String
    Public ReadOnly host_or_ip As String
    Public ReadOnly port As UInt16
    Public ReadOnly connecting_timeout_ms As Int64
    Public ReadOnly send_rate_sec As UInt32
    Public ReadOnly response_timeout_ms As Int64
    Public ReadOnly receive_rate_sec As UInt32
    Public ReadOnly max_connecting As UInt32
    Public ReadOnly max_lifetime_ms As Int64
    Public ReadOnly no_delay As Boolean
    Public ReadOnly is_outgoing As Boolean
    Public ReadOnly enable_keepalive As Boolean
    Public ReadOnly first_keepalive_ms As UInt32
    Public ReadOnly keepalive_interval_ms As UInt32
    Public ReadOnly ipv4 As Boolean
    Public ReadOnly raw_block_device_pool As new_device_event_isync_device_pool(Of block)
    Public ReadOnly block_device_pool As new_device_event_isync_device_pool(Of block)
    Public ReadOnly herald_device_pool As new_device_event_isync_device_pool(Of herald)
    Friend ReadOnly all_clients As qless2(Of TcpClient)
    Private ReadOnly pool As pre_generated_device_pool(Of connection)
    Private ReadOnly mpool As manual_pre_generated_device_pool(Of connection)
    Private ReadOnly apool As auto_pre_generated_device_pool(Of connection)
    Private ReadOnly header_data_increment As UInt32

    Private Sub New(ByVal token As String,
                    ByVal host_or_ip As String,
                    ByVal port As UInt32,
                    ByVal ipv4 As Boolean,
                    ByVal connecting_timeout_ms As Int64,
                    ByVal send_rate_sec As UInt32,
                    ByVal response_timeout_ms As Int64,
                    ByVal receive_rate_sec As UInt32,
                    ByVal max_connecting As UInt32,
                    ByVal max_connected As UInt32,
                    ByVal no_delay As Boolean,
                    ByVal max_lifetime_ms As Int64,
                    ByVal is_outgoing As Boolean,
                    ByVal enable_keepalive As Boolean,
                    ByVal first_keepalive_ms As UInt32,
                    ByVal keepalive_interval_ms As UInt32,
                    ByVal v As var)
        Me.token = token
        assert(max_connecting > 0)
        assert(max_connected > 0)
        assert(max_connecting <= max_connected)
        assert(is_outgoing Xor String.IsNullOrEmpty(host_or_ip))

        Me.host_or_ip = host_or_ip
        Me.port = port
        Me.ipv4 = ipv4
        Me.connecting_timeout_ms = connecting_timeout_ms
        Me.send_rate_sec = send_rate_sec
        Me.response_timeout_ms = response_timeout_ms
        Me.receive_rate_sec = receive_rate_sec
        Me.max_connecting = max_connecting
        Me.no_delay = no_delay
        Me.max_lifetime_ms = max_lifetime_ms
        Me.is_outgoing = is_outgoing
        Me.enable_keepalive = enable_keepalive
        Me.first_keepalive_ms = first_keepalive_ms
        Me.keepalive_interval_ms = keepalive_interval_ms
        Me.raw_block_device_pool = New flow_block_new_device_event_isync_device_pool(Me)
        If v Is Nothing Then
            Me.block_device_pool = raw_block_device_pool
        Else
            Me.block_device_pool = New block_new_device_event_isync_device_pool_wrapper(raw_block_device_pool, v)
        End If
        Me.herald_device_pool = New block_herald_new_device_event_isync_device_pool(block_device_pool)
        Me.all_clients = New qless2(Of TcpClient)()

        Dim new_connection_generated_action As Action(Of connection) =
                Sub(c As connection)
                    assert(Not c Is Nothing)
                    cleanup_all_clients()
                    Me.all_clients.push(c.no_refer_client())
                End Sub
        If is_outgoing Then
            Me.apool = New auto_pre_generated_device_pool(Of connection) _
                                                         (max_connected,
                                                          identity(),
                                                          AddressOf generate_connection,
                                                          constants.interval_ms.connector_check,
                                                          constants.interval_ms.connector_fail,
                                                          max_connecting,
                                                          AddressOf connection.validate,
                                                          AddressOf connection.close,
                                                          Function(c As connection) As String
                                                              assert(Not c Is Nothing)
                                                              Return c.identity
                                                          End Function,
                                                          new_connection_generated_action)
            Me.pool = Me.apool
        Else
            Me.mpool = New manual_pre_generated_device_pool(Of connection) _
                                                           (max_connected,
                                                            identity(),
                                                            AddressOf connection.validate,
                                                            AddressOf connection.close,
                                                            Function(c As connection) As String
                                                                assert(Not c Is Nothing)
                                                                Return c.identity
                                                            End Function,
                                                            new_connection_generated_action)
            Me.pool = Me.mpool
        End If
        assert(Not Me.pool Is Nothing)
        AddHandler Me.pool.new_device_inserted,
                   Sub()
                       RaiseEvent new_device_inserted()
                   End Sub
        AddHandler Me.pool.closing,
                   Sub()
                       RaiseEvent closing()
                   End Sub

        Me.header_data_increment = If(ipv4, 40, 60)
    End Sub

    Private Sub cleanup_all_clients()
        Dim cc As Int64 = 0
        cc = all_clients.size()
        Dim c As TcpClient = Nothing
        While cc > 0
            If all_clients.pop(c) Then
                assert(Not c Is Nothing)
                If c.alive() Then
                    all_clients.push(c)
                End If
                cc -= 1
            Else
                Exit While
            End If
        End While
    End Sub

    Public Function max_connected() As UInt32
        Return pool.max_count()
    End Function

    Public Function connected_count() As UInt32
        Return pool.total_count()
    End Function

    Public Function send_timeout_ms(ByVal count As Int64) As Int64
        Return rate_to_ms(send_rate_sec, count + header_data_increment)
    End Function

    Public Function receive_timeout_ms(ByVal count As Int64) As Int64
        Return rate_to_ms(receive_rate_sec, count + header_data_increment)
    End Function

    Private Function generate_connection(ByVal r As pointer(Of connection)) As event_comb
        Dim ec As event_comb = Nothing
        Dim p As pointer(Of TcpClient) = Nothing
        Return New event_comb(Function() As Boolean
                                  p = New pointer(Of TcpClient)()
                                  ec = connector.generate_connection(Me, p)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         eva(r, New connection(+p)) AndAlso
                                         goto_end()
                              End Function)
    End Function

    'for closer only
    Friend Function [get](ByRef r As connection) As Boolean
        Return pool.sync_get(r)
    End Function

    'for closer only
    Friend Function release(ByVal c As connection) As Boolean
        assert(Not c Is Nothing)
        If pool.release(c) Then
            Return True
        Else
            If tcp_trace Then
                raise_error("connection ",
                            c.identity,
                            " has been removed from powerpoint ",
                            identity())
            End If
            Return False
        End If
    End Function

    Public Function get_all(ByVal v As Action(Of TcpClient, powerpoint)) As Boolean
        If v Is Nothing Then
            Return False
        Else
            Return pool.get_all(Sub(c As connection)
                                    assert(Not c Is Nothing)
                                    v(c.client(), Me)
                                End Sub)
        End If
    End Function

    Public Function get_all(ByVal v As Action(Of TcpClient)) As Boolean
        If v Is Nothing Then
            Return False
        Else
            Return pool.get_all(Sub(c As connection)
                                    assert(Not c Is Nothing)
                                    v(c.client())
                                End Sub)
        End If
    End Function

    Public Function get_all(ByVal v As Func(Of TcpClient, powerpoint, event_comb)) As Boolean
        If v Is Nothing Then
            Return False
        Else
            Return pool.get_all(Function(c As connection) As event_comb
                                    assert(Not c Is Nothing)
                                    Return v(c.client(), Me)
                                End Function)
        End If
    End Function

    Public Function get_all(ByVal v As Func(Of TcpClient, event_comb)) As Boolean
        If v Is Nothing Then
            Return False
        Else
            Return pool.get_all(Function(c As connection) As event_comb
                                    assert(Not c Is Nothing)
                                    Return v(c.client())
                                End Function)
        End If
    End Function

    'for accepter / connector
    Friend Function insert(ByVal client As TcpClient) As Boolean
        assert(Not mpool Is Nothing)
        If mpool.insert(New connection(client)) Then
            If tcp_trace Then
                raise_error("new conneciton ",
                            client.identity(),
                            " has been generated to the powerpoint ",
                            identity())
            End If
            client.set_no_delay(no_delay)
            client.set_keepalive(enable_keepalive, first_keepalive_ms, keepalive_interval_ms)
            Return True
        Else
            If tcp_trace Then
                raise_error("cannot insert new connection ",
                            client.identity(),
                            " to the powerpoint ",
                            identity(),
                            ", because of the connection count limitation ",
                            "or the connection has been shutdown already")
            End If
            Return False
        End If
    End Function

    'test purpose only, make sure all the related event_combs and stopwatch events are stopped
    Public Shared Sub waitfor_stop(ByVal ParamArray this() As powerpoint)
        sleep(stop_milliseconds(this))
    End Sub

    'the longest time in milliseconds to let all the related event_combs stop
    Public Shared Function stop_milliseconds(ByVal ParamArray this() As powerpoint) As Int64
        Dim m As Int64 = 0
        m = max(constants.interval_ms.watch_connectivity,
                constants.interval_ms.watch_expiration)
        m = max(m, constants.interval_ms.connector_check)
        m = max(m, constants.interval_ms.connector_fail)
        For i As Int32 = 0 To array_size(this) - 1
            If Not this(i) Is Nothing AndAlso
               this(i).is_outgoing Then
                m = max(m, this(i).connecting_timeout_ms)
            End If
        Next
        'accepter should be finished almost immediately
        Return m
    End Function
End Class
